﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows;

namespace epk23.PartIIProject
{
    abstract class RuleNode
    {
        public SolidColorBrush fillBrush;
        protected Canvas canvas;
        public Rectangle nodeBody;
        public Label deleteLabel { get; set; }
        public Label label { get; set; }
        public static MachineWindow window { get; set; }
        public static MachineGraph graph { get; set; }

        public double x { get; set; }
        public double y { get; set; }

        public RuleNode(double x, double y, Canvas canvas)
        {
            this.canvas = canvas;

            nodeBody = new Rectangle();
            nodeBody.Width = Values.NodeWidth;
            nodeBody.Height = Values.NodeHeight;
            nodeBody.IsHitTestVisible = true;
            fillBrush = new SolidColorBrush(System.Windows.Media.Colors.White);
            fillBrush.Opacity = 0.6;
            nodeBody.Stroke = System.Windows.Media.Brushes.Black;
            nodeBody.Fill = fillBrush;
            nodeBody.SetValue(Canvas.ZIndexProperty, 10);

            deleteLabel = new Label();
            deleteLabel.Width = Values.DeleteLabelSize;
            deleteLabel.Height = Values.DeleteLabelSize;
            deleteLabel.FontSize = 9;
            deleteLabel.FontWeight = FontWeights.Bold;
            deleteLabel.HorizontalContentAlignment = HorizontalAlignment.Right;
            deleteLabel.VerticalContentAlignment = VerticalAlignment.Top;
            deleteLabel.BorderThickness = new System.Windows.Thickness(1, 1, 1, 1);
            deleteLabel.BorderBrush = System.Windows.Media.Brushes.Black;
            deleteLabel.Content = "X";
            deleteLabel.IsHitTestVisible = true;
            deleteLabel.Visibility = System.Windows.Visibility.Hidden;
            deleteLabel.SetValue(Canvas.ZIndexProperty, 11);

            nodeBody.MouseDown += (MouseButtonEventHandler)graph.nodeBodyMouseDown;
            nodeBody.MouseMove += (MouseEventHandler)graph.nodeBodyMouseMove;
            nodeBody.MouseUp += (MouseButtonEventHandler)graph.nodeBodyMouseUp;
            nodeBody.MouseEnter += (MouseEventHandler)graph.nodeBodyMouseEnter;
            nodeBody.MouseLeave += (MouseEventHandler)graph.nodeBodyMouseLeave;
            deleteLabel.MouseUp += (MouseButtonEventHandler)graph.deleteLabelMouseUp;
            deleteLabel.MouseEnter += (MouseEventHandler)graph.deleteLabelMouseEnter;
            deleteLabel.MouseLeave += (MouseEventHandler)graph.deleteLabelMouseLeave;

            graph.addNode(this);
        }

        public void setPosition(double x, double y)
        {
            this.x = x;
            this.y = y;

            nodeBody.SetValue(Canvas.LeftProperty, x);
            nodeBody.SetValue(Canvas.TopProperty, y);

            deleteLabel.SetValue(Canvas.LeftProperty, (double)(x + (nodeBody.Width - Values.DeleteLabelSize)));
            deleteLabel.SetValue(Canvas.TopProperty, (double)y);
        }

        public void addToCanvas()
        {
            canvas.Children.Add(nodeBody);
            canvas.Children.Add(deleteLabel);
            canvas.Children.Add(label);

            this.setPosition(x, y);
        }


        public void delete()
        {
            canvas.Children.Remove(nodeBody);
            canvas.Children.Remove(label);
            canvas.Children.Remove(deleteLabel);

            foreach (GraphArrow arrow in graph.Arrows)
            {
                if (arrow.startNode == this)
                    canvas.Children.Remove(arrow.arrow);

                if (arrow.endNode == this)
                {
                    arrow.endNode = arrow.startNode;
                    arrow.startAnchor = 3;
                    if (arrow.type == 1)
                        arrow.endAnchor = 4;
                    else
                        arrow.endAnchor = 2;

                    if (arrow.startNode.GetType() == typeof(IncrRuleNode))
                    {
                        IncrRuleNode startNode = (IncrRuleNode)arrow.startNode;
                        startNode.rule.targetRuleNumber = startNode.rule.ruleNumber;
                    }

                    else if (arrow.startNode.GetType() == typeof(DecrRuleNode))
                    {
                        DecrRuleNode startNode = (DecrRuleNode)arrow.startNode;
                        startNode.rule.targetRuleNumber = startNode.rule.ruleNumber;
                        startNode.rule.targetRuleNumber2 = startNode.rule.ruleNumber;
                    }

                    arrow.setPosition();
                }
            }
        }
    }

    abstract class RegisterNode : RuleNode
    {
        public RegisterNode(double x, double y, string regName, Canvas canvas)
            : base(x, y, canvas)
        {
            label = new Label();
            label.Height = 30;
            label.Width = 35;
            label.FontSize = 14;
            label.HorizontalContentAlignment = HorizontalAlignment.Center;
            label.IsHitTestVisible = false;
            label.SetValue(Canvas.ZIndexProperty, 11);

            graph.machine.addRegister(regName);
        }

        new public void setPosition(double x, double y)
        {
            base.setPosition(x, y);

            label.SetValue(Canvas.LeftProperty, (double)(x + (nodeBody.Width / 2) - (label.Width / 2)));
            label.SetValue(Canvas.TopProperty, (double)(y + (nodeBody.Height / 2) - (label.Height/2)));
        }
    }

    class IncrRuleNode : RegisterNode, ConcreteNode
    {
        public IncrementRule rule { get; set; }
        public Rule getRule()
        {
            return rule;
        }

        public int getTargetRuleNumber()
        {
            return rule.targetRuleNumber;
        }

        private RuleNode targetNode;

        public IncrRuleNode(double x, double y, string regName, Canvas canvas)
            : base(x, y, regName, canvas)
        {
            label.Content = regName + "+";
            targetNode = this;

            setPosition(x, y);

            int ruleNumber = graph.getNextRuleNumber();
            rule = new IncrementRule(ruleNumber, regName, ruleNumber);
        }
    }

    class DecrRuleNode : RegisterNode, ConcreteNode
    {
        public DecrementRule rule { get; set; }
        public Rule getRule()
        {
            return rule;
        }

        public int getTargetRuleNumber()
        {
            return rule.targetRuleNumber;
        }

        private RuleNode targetNode1;
        private RuleNode targetNode2;
        public DecrRuleNode(double x, double y, string regName, Canvas canvas)
            : base(x, y, regName, canvas)
        {
            label.Content = regName + "-";

            setPosition(x, y);

            targetNode1 = this;
            targetNode2 = this;

            int ruleNumber = graph.getNextRuleNumber();
            rule = new DecrementRule(ruleNumber, regName, ruleNumber, ruleNumber);
        }
    }

    abstract class LabelNode : RuleNode
    {
        public LabelNode(double x, double y, Canvas canvas)
            : base(x, y, canvas)
        {
            label = new Label();
            label.Height = 25;
            label.Width = 45;
            label.BorderThickness = new System.Windows.Thickness(1, 1, 1, 1);
            label.BorderBrush = System.Windows.Media.Brushes.Black;
            label.IsHitTestVisible = false;
            label.SetValue(Canvas.ZIndexProperty, 11);
        }

        new public void setPosition(double x, double y)
        {
            base.setPosition(x, y);

            label.SetValue(Canvas.LeftProperty, (double)(x + (nodeBody.Width / 2) - (label.Width / 2)));
            label.SetValue(Canvas.TopProperty, (double)(y + (nodeBody.Height / 2) - (label.Height / 2)));
        }
    }

    class HaltRuleNode : LabelNode, ConcreteNode
    {
        public HaltRule rule { get; set; }
        public Rule getRule()
        {
            return rule;
        }

        public int getTargetRuleNumber()
        {
            return rule.targetRuleNumber;
        }

        public HaltRuleNode(double x, double y, Canvas canvas)
            : base(x, y, canvas)
        {
            label.Content = "HALT";

            setPosition(x, y);

            rule = new HaltRule(graph.getNextRuleNumber());
        }
    }

    class MachineNode : LabelNode, ConcreteNode
    {
        private String machineName;
        public MachineRule rule { get; set; }

        public string description { get; set; }

        public MachineNode(double x, double y, string machineName, string description, Canvas canvas)
            : base(x, y, canvas)
        {
            this.machineName = machineName;
            this.description = description;

            setPosition(x, y);
            label.Content = this.machineName;

            int ruleNumber = graph.getNextRuleNumber();
            rule = new MachineRule(ruleNumber, machineName, ruleNumber);
        }

        public Rule getRule()
        {
            return rule;
        }

        public int getTargetRuleNumber()
        {
            return rule.targetRuleNumber;
        }
    }
}

